NAMES - MOHAMED BENKHIAL, AKINDELE DAVID AWE¶

PROJECT - HUMAN SEGMENTATION¶

DATASET SOURCES - https://www.kaggle.com/datasets/tapakah68/segmentation-full-body-mads-dataset, https://www.kaggle.com/code/arunkothari/image-segmentaion-unet-99-acc

In [ ]:
import tensorflow as tf
import numpy as np
import os
import matplotlib.pyplot as plt
from PIL import Image
In [53]:
IMG_CHANNELS, IMG_WIDTH, IMG_HEIGHT = 3, 512, 512
In [54]:
X = next(os.walk('C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/images'))[2]
y = next(os.walk('C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/masks'))[2]
In [55]:
X_ids = X[:-10]
y_ids = y[:-10]
In [56]:
import os
import numpy as np
import tensorflow as tf

# Initialize arrays with appropriate shapes
X_train = np.zeros((len(X_ids), 256, 256, 3), dtype=np.float32)
y_train = np.zeros((len(y_ids), 256, 256, 1), dtype=bool)  # Use bool instead of np.bool

# Loop through the image IDs and load the images for X_train
for n, id_ in enumerate(X_ids):
    image_path = f'C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/images/{id_}'
    if os.path.exists(image_path):
        image = tf.keras.preprocessing.image.load_img(image_path, target_size=(IMG_HEIGHT, IMG_WIDTH))
        input_arr = tf.keras.preprocessing.image.img_to_array(image)[90:450,150:406]
        image = tf.keras.preprocessing.image.array_to_img(input_arr).resize((256, 256))
        X_train[n] = np.array(image)
    else:
        print(f"Image file not found: {image_path}")

# Loop through the mask IDs and load the images for y_train
for n, id_ in enumerate(y_ids):
    mask_path = f'C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/masks/{id_}'
    if os.path.exists(mask_path):
        image = tf.keras.preprocessing.image.load_img(mask_path, target_size=(IMG_HEIGHT, IMG_WIDTH), color_mode="grayscale")
        input_arr = tf.keras.preprocessing.image.img_to_array(image)[90:450,150:406]
        image = tf.keras.preprocessing.image.array_to_img(input_arr).resize((256, 256))
        y_train[n] = np.array(image)[:, :, np.newaxis]  # Add new axis for channel dimension
    else:
        print(f"Mask file not found: {mask_path}")
In [13]:
plt.imshow(tf.keras.preprocessing.image.array_to_img(X_train[1000]))
Out[13]:
<matplotlib.image.AxesImage at 0x207202bd050>
In [57]:
plt.imshow(tf.keras.preprocessing.image.array_to_img(y_train[1000]))
Out[57]:
<matplotlib.image.AxesImage at 0x2286b9b4310>
In [15]:
inputs = tf.keras.layers.Input((256, 256, 3))
s = tf.keras.layers.Lambda(lambda x: x / 255)(inputs)

#Contraction path
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(s)
c1 = tf.keras.layers.Dropout(0.1)(c1)
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c1)
p1 = tf.keras.layers.MaxPooling2D((2, 2))(c1)

c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p1)
c2 = tf.keras.layers.Dropout(0.1)(c2)
c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c2)
p2 = tf.keras.layers.MaxPooling2D((2, 2))(c2)
 
c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p2)
c3 = tf.keras.layers.Dropout(0.2)(c3)
c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c3)
p3 = tf.keras.layers.MaxPooling2D((2, 2))(c3)
 
c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p3)
c4 = tf.keras.layers.Dropout(0.2)(c4)
c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c4)
p4 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(c4)
 
c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p4)
c5 = tf.keras.layers.Dropout(0.3)(c5)
c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c5)

#Expansive path 
u6 = tf.keras.layers.Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c5)
u6 = tf.keras.layers.concatenate([u6, c4])
c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u6)
c6 = tf.keras.layers.Dropout(0.2)(c6)
c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c6)
 
u7 = tf.keras.layers.Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c6)
u7 = tf.keras.layers.concatenate([u7, c3])
c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u7)
c7 = tf.keras.layers.Dropout(0.2)(c7)
c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c7)
 
u8 = tf.keras.layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c7)
u8 = tf.keras.layers.concatenate([u8, c2])
c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u8)
c8 = tf.keras.layers.Dropout(0.1)(c8)
c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c8)
 
u9 = tf.keras.layers.Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c8)
u9 = tf.keras.layers.concatenate([u9, c1], axis=3)
c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u9)
c9 = tf.keras.layers.Dropout(0.1)(c9)
c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c9)
 
outputs = tf.keras.layers.Conv2D(1, (1, 1), activation='sigmoid')(c9)
 
model = tf.keras.Model(inputs=[inputs], outputs=[outputs])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model.summary()
WARNING:tensorflow:From c:\Users\binkh\Documents\CSCN8010\CSSN8010\venv\tensorflow_cpu\Lib\site-packages\keras\src\backend\tensorflow\core.py:222: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.

Model: "functional"
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃ Layer (type)        ┃ Output Shape      ┃    Param # ┃ Connected to      ┃
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ input_layer         │ (None, 256, 256,  │          0 │ -                 │
│ (InputLayer)        │ 3)                │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ lambda (Lambda)     │ (None, 256, 256,  │          0 │ input_layer[0][0] │
│                     │ 3)                │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d (Conv2D)     │ (None, 256, 256,  │        448 │ lambda[0][0]      │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout (Dropout)   │ (None, 256, 256,  │          0 │ conv2d[0][0]      │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_1 (Conv2D)   │ (None, 256, 256,  │      2,320 │ dropout[0][0]     │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d       │ (None, 128, 128,  │          0 │ conv2d_1[0][0]    │
│ (MaxPooling2D)      │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_2 (Conv2D)   │ (None, 128, 128,  │      4,640 │ max_pooling2d[0]… │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_1 (Dropout) │ (None, 128, 128,  │          0 │ conv2d_2[0][0]    │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_3 (Conv2D)   │ (None, 128, 128,  │      9,248 │ dropout_1[0][0]   │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d_1     │ (None, 64, 64,    │          0 │ conv2d_3[0][0]    │
│ (MaxPooling2D)      │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_4 (Conv2D)   │ (None, 64, 64,    │     18,496 │ max_pooling2d_1[… │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_2 (Dropout) │ (None, 64, 64,    │          0 │ conv2d_4[0][0]    │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_5 (Conv2D)   │ (None, 64, 64,    │     36,928 │ dropout_2[0][0]   │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d_2     │ (None, 32, 32,    │          0 │ conv2d_5[0][0]    │
│ (MaxPooling2D)      │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_6 (Conv2D)   │ (None, 32, 32,    │     73,856 │ max_pooling2d_2[… │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_3 (Dropout) │ (None, 32, 32,    │          0 │ conv2d_6[0][0]    │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_7 (Conv2D)   │ (None, 32, 32,    │    147,584 │ dropout_3[0][0]   │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d_3     │ (None, 16, 16,    │          0 │ conv2d_7[0][0]    │
│ (MaxPooling2D)      │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_8 (Conv2D)   │ (None, 16, 16,    │    295,168 │ max_pooling2d_3[… │
│                     │ 256)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_4 (Dropout) │ (None, 16, 16,    │          0 │ conv2d_8[0][0]    │
│                     │ 256)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_9 (Conv2D)   │ (None, 16, 16,    │    590,080 │ dropout_4[0][0]   │
│                     │ 256)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_transpose    │ (None, 32, 32,    │    131,200 │ conv2d_9[0][0]    │
│ (Conv2DTranspose)   │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate         │ (None, 32, 32,    │          0 │ conv2d_transpose… │
│ (Concatenate)       │ 256)              │            │ conv2d_7[0][0]    │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_10 (Conv2D)  │ (None, 32, 32,    │    295,040 │ concatenate[0][0] │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_5 (Dropout) │ (None, 32, 32,    │          0 │ conv2d_10[0][0]   │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_11 (Conv2D)  │ (None, 32, 32,    │    147,584 │ dropout_5[0][0]   │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_transpose_1  │ (None, 64, 64,    │     32,832 │ conv2d_11[0][0]   │
│ (Conv2DTranspose)   │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate_1       │ (None, 64, 64,    │          0 │ conv2d_transpose… │
│ (Concatenate)       │ 128)              │            │ conv2d_5[0][0]    │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_12 (Conv2D)  │ (None, 64, 64,    │     73,792 │ concatenate_1[0]… │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_6 (Dropout) │ (None, 64, 64,    │          0 │ conv2d_12[0][0]   │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_13 (Conv2D)  │ (None, 64, 64,    │     36,928 │ dropout_6[0][0]   │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_transpose_2  │ (None, 128, 128,  │      8,224 │ conv2d_13[0][0]   │
│ (Conv2DTranspose)   │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate_2       │ (None, 128, 128,  │          0 │ conv2d_transpose… │
│ (Concatenate)       │ 64)               │            │ conv2d_3[0][0]    │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_14 (Conv2D)  │ (None, 128, 128,  │     18,464 │ concatenate_2[0]… │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_7 (Dropout) │ (None, 128, 128,  │          0 │ conv2d_14[0][0]   │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_15 (Conv2D)  │ (None, 128, 128,  │      9,248 │ dropout_7[0][0]   │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_transpose_3  │ (None, 256, 256,  │      2,064 │ conv2d_15[0][0]   │
│ (Conv2DTranspose)   │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate_3       │ (None, 256, 256,  │          0 │ conv2d_transpose… │
│ (Concatenate)       │ 32)               │            │ conv2d_1[0][0]    │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_16 (Conv2D)  │ (None, 256, 256,  │      4,624 │ concatenate_3[0]… │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_8 (Dropout) │ (None, 256, 256,  │          0 │ conv2d_16[0][0]   │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_17 (Conv2D)  │ (None, 256, 256,  │      2,320 │ dropout_8[0][0]   │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_18 (Conv2D)  │ (None, 256, 256,  │         17 │ conv2d_17[0][0]   │
│                     │ 1)                │            │                   │
└─────────────────────┴───────────────────┴────────────┴───────────────────┘
 Total params: 1,941,105 (7.40 MB)
 Trainable params: 1,941,105 (7.40 MB)
 Non-trainable params: 0 (0.00 B)

Input Normalization:¶

The input image, of size 256 × 256 × 3 256×256×3, is normalized by dividing its pixel values by 255.

Contraction Path (Encoder): This reduces the spatial dimensions while increasing feature depth:

Block 1: Two convolutional layers with ReLU activation, followed by max pooling. Block 2: Similar to Block 1 but with 32 filters. Block 3 and Block 4: Follow the same pattern with increasing filters (64, 128). Bottom Layer: Processes features at the smallest spatial scale (256 filters). Expansive Path (Decoder): This upsamples feature maps to reconstruct the input’s dimensions:

Upsampling: Transposed convolutions double spatial dimensions. Skip Connections: Concatenate encoder features with upsampled features for rich context. Convolutions: Repeated convolutional layers refine features. Output: A final 1 × 1 1×1 convolution predicts segmentation probabilities for each pixel (sigmoid activation for binary segmentation).

Model Compilation: The model uses Adam optimizer, binary cross-entropy loss, and accuracy as a metric.

Summary: The model.summary() displays the architecture, parameters, and layers.

In [17]:
results = model.fit(X_train, y_train, validation_split=0.1, batch_size=16, epochs=25)
Epoch 1/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 53s 793ms/step - accuracy: 0.8780 - loss: 0.3876 - val_accuracy: 0.8393 - val_loss: 0.2892
Epoch 2/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 816ms/step - accuracy: 0.8936 - loss: 0.2163 - val_accuracy: 0.9200 - val_loss: 0.1709
Epoch 3/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 112s 2s/step - accuracy: 0.9645 - loss: 0.0908 - val_accuracy: 0.9518 - val_loss: 0.1090
Epoch 4/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 195s 3s/step - accuracy: 0.9751 - loss: 0.0643 - val_accuracy: 0.9697 - val_loss: 0.0790
Epoch 5/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 184s 3s/step - accuracy: 0.9827 - loss: 0.0443 - val_accuracy: 0.9758 - val_loss: 0.0618
Epoch 6/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 189s 3s/step - accuracy: 0.9842 - loss: 0.0408 - val_accuracy: 0.9787 - val_loss: 0.0543
Epoch 7/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 155s 2s/step - accuracy: 0.9870 - loss: 0.0329 - val_accuracy: 0.9804 - val_loss: 0.0490
Epoch 8/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 92s 1s/step - accuracy: 0.9886 - loss: 0.0286 - val_accuracy: 0.9818 - val_loss: 0.0468
Epoch 9/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 56s 828ms/step - accuracy: 0.9887 - loss: 0.0284 - val_accuracy: 0.9831 - val_loss: 0.0422
Epoch 10/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 826ms/step - accuracy: 0.9894 - loss: 0.0264 - val_accuracy: 0.9849 - val_loss: 0.0385
Epoch 11/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 56s 831ms/step - accuracy: 0.9902 - loss: 0.0242 - val_accuracy: 0.9841 - val_loss: 0.0411
Epoch 12/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 56s 827ms/step - accuracy: 0.9903 - loss: 0.0244 - val_accuracy: 0.9852 - val_loss: 0.0387
Epoch 13/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 819ms/step - accuracy: 0.9909 - loss: 0.0225 - val_accuracy: 0.9857 - val_loss: 0.0369
Epoch 14/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 824ms/step - accuracy: 0.9913 - loss: 0.0213 - val_accuracy: 0.9858 - val_loss: 0.0375
Epoch 15/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 56s 829ms/step - accuracy: 0.9913 - loss: 0.0213 - val_accuracy: 0.9866 - val_loss: 0.0353
Epoch 16/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 56s 831ms/step - accuracy: 0.9918 - loss: 0.0202 - val_accuracy: 0.9866 - val_loss: 0.0358
Epoch 17/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 54s 809ms/step - accuracy: 0.9920 - loss: 0.0196 - val_accuracy: 0.9855 - val_loss: 0.0435
Epoch 18/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 824ms/step - accuracy: 0.9917 - loss: 0.0204 - val_accuracy: 0.9871 - val_loss: 0.0338
Epoch 19/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 816ms/step - accuracy: 0.9921 - loss: 0.0192 - val_accuracy: 0.9873 - val_loss: 0.0336
Epoch 20/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 818ms/step - accuracy: 0.9924 - loss: 0.0185 - val_accuracy: 0.9872 - val_loss: 0.0335
Epoch 21/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 56s 842ms/step - accuracy: 0.9924 - loss: 0.0185 - val_accuracy: 0.9877 - val_loss: 0.0320
Epoch 22/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 58s 873ms/step - accuracy: 0.9926 - loss: 0.0181 - val_accuracy: 0.9875 - val_loss: 0.0325
Epoch 23/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 821ms/step - accuracy: 0.9926 - loss: 0.0180 - val_accuracy: 0.9879 - val_loss: 0.0334
Epoch 24/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 821ms/step - accuracy: 0.9926 - loss: 0.0180 - val_accuracy: 0.9874 - val_loss: 0.0338
Epoch 25/25
67/67 ━━━━━━━━━━━━━━━━━━━━ 55s 817ms/step - accuracy: 0.9922 - loss: 0.0191 - val_accuracy: 0.9872 - val_loss: 0.0359
In [18]:
# summarize history for accuracy
plt.plot(results.history['accuracy'])
plt.plot(results.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()

# summarize history for loss
plt.plot(results.history['loss'])
plt.plot(results.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
In [44]:
import random

test_id = random.choice(X_ids[-10:])
print(test_id)
Taichi_S6_C1_00270.png
In [45]:
img = tf.keras.preprocessing.image.load_img(f"C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/images/{test_id}", target_size=(256, 256))
input_array = tf.keras.preprocessing.image.img_to_array(img)
input_array = np.array([input_array])  # Convert single image to a batch.
predictions = model.predict(input_array)
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 61ms/step
In [46]:
Image.open(f"C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/images/{test_id}").resize((256, 256))
Out[46]:
In [48]:
plt.imshow(tf.keras.preprocessing.image.array_to_img(np.squeeze(predictions)[:, :, np.newaxis]))
Out[48]:
<matplotlib.image.AxesImage at 0x207460e1590>
In [49]:
Image.open(f"C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/masks/{test_id}").resize((256 ,256))
Out[49]:
In [55]:
plt.figure(figsize=(6, 6))
plt.imshow(ground_truth_array, cmap="gray", alpha=0.5)
plt.imshow(predicted_mask, cmap="jet", alpha=0.5)  # Overlay the predicted mask on the ground truth
plt.title("Overlay of Predicted Mask and Ground Truth Mask")
plt.show()
In [56]:
plt.imshow(predicted_mask, cmap="gray")  # See the raw predicted mask (before thresholding)
plt.title("Raw Predicted Mask")
plt.show()
In [58]:
import random
import numpy as np
import tensorflow as tf
from tensorflow.keras.metrics import MeanIoU
import matplotlib.pyplot as plt

# Randomly select a test ID from the last 10 IDs in the dataset
test_id = random.choice(X_ids[-10:])
print(f"Test ID: {test_id}")

# Load the image and resize it to (256, 256)
img_path = f"C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/images/{test_id}"
img = tf.keras.preprocessing.image.load_img(img_path, target_size=(256, 256))
input_array = tf.keras.preprocessing.image.img_to_array(img)
input_array = np.array([input_array])  # Convert single image to a batch

# Predict the mask using the model
predictions = model.predict(input_array)  # Predictions shape: (1, 256, 256, 1)

# Remove batch dimension and the extra channels, make it (256, 256)
predicted_mask = np.squeeze(predictions)  # Shape: (256, 256)

# Apply a threshold to the predicted mask to convert it to binary (0 or 1)
threshold = 0.5  # You can adjust this if necessary
predicted_mask_binary = (predicted_mask > threshold).astype(np.uint8)

# Load the ground truth mask and resize it to (256, 256)
mask_path = f"C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/masks/{test_id}"
ground_truth_mask = tf.keras.preprocessing.image.load_img(mask_path, target_size=(256, 256), color_mode="grayscale")
ground_truth_array = tf.keras.preprocessing.image.img_to_array(ground_truth_mask)
ground_truth_array = (ground_truth_array > 0.5).astype(np.uint8)  # Convert to binary mask

# Reshape the masks to be (256, 256, 1) for IoU calculation
ground_truth_reshaped = np.expand_dims(ground_truth_array, axis=-1)  # Shape: (256, 256, 1)
predicted_mask_reshaped = np.expand_dims(predicted_mask_binary, axis=-1)  # Shape: (256, 256, 1)

# Define and calculate IoU using Keras MeanIoU metric
n_classes = 2  # 2 classes: background (0) and foreground (1)
IOU_keras = MeanIoU(num_classes=n_classes)

# Update the state with the ground truth and predicted mask
IOU_keras.update_state(ground_truth_reshaped, predicted_mask_reshaped)

# Calculate the mean IoU
print("Mean IoU =", IOU_keras.result().numpy())

# Display the images for visualization
fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# Original image
axes[0].imshow(img)
axes[0].set_title("Original Image")
axes[0].axis("off")

# Predicted mask
axes[1].imshow(predicted_mask_binary, cmap="gray")
axes[1].set_title("Predicted Mask")
axes[1].axis("off")

# Ground truth mask
axes[2].imshow(ground_truth_array, cmap="gray")
axes[2].set_title("Ground Truth Mask")
axes[2].axis("off")

plt.show()
Test ID: Taichi_S6_C1_00270.png
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 67ms/step
Mean IoU = 0.86715734

__¶

In [59]:
# Plot original image with overlaid predicted mask
plt.figure(figsize=(8, 8))
plt.imshow(img)
plt.imshow(predicted_mask_binary, cmap="jet", alpha=0.5)  # Overlay with transparency
plt.title("Original Image with Predicted Mask Overlay")
plt.axis('off')
plt.show()

Further training to run on external images¶

In [18]:
import os
import numpy as np
import tensorflow as tf
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from PIL import Image

# Constants
IMG_SIZE = (256, 256)  # Image and mask size
DATASET_DIR = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img"
IMAGES_DIR = os.path.join(DATASET_DIR, "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/images")
MASKS_DIR = os.path.join(DATASET_DIR, "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/masks")
In [19]:
import os

# Paths to images and masks
image_dir = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/images"
mask_dir = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/masks"

# List files in image and mask directories
image_files = os.listdir(image_dir)
mask_files = os.listdir(mask_dir)

print(f"Number of images: {len(image_files)}")
print(f"Number of masks: {len(mask_files)}")

# Check if file names match (you might need to ensure the names are correctly paired)
print(f"First image file: {image_files[0]}")
print(f"First mask file: {mask_files[0]}")
Number of images: 1192
Number of masks: 1192
First image file: HipHop_HipHop1_C0_00180.png
First mask file: HipHop_HipHop1_C0_00180.png
In [20]:
import matplotlib.pyplot as plt
import cv2

# Load a sample image and its corresponding mask
sample_image_path = os.path.join(image_dir, image_files[0])
sample_mask_path = os.path.join(mask_dir, mask_files[0])

sample_image = cv2.imread(sample_image_path)
sample_mask = cv2.imread(sample_mask_path, cv2.IMREAD_GRAYSCALE)

# image and its mask plotting
plt.figure(figsize=(10, 5))

plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(sample_image, cv2.COLOR_BGR2RGB))
plt.title("Sample Image")
plt.axis("off")

plt.subplot(1, 2, 2)
plt.imshow(sample_mask, cmap="gray")
plt.title("Corresponding Mask")
plt.axis("off")

plt.tight_layout()
plt.show()
In [21]:
import random

# Display random pairs of images and masks (show 5 pairs)
num_samples = 5
plt.figure(figsize=(15, 15))

for i in range(num_samples):
    random_idx = random.randint(0, len(image_files)-1)
    
    sample_image_path = os.path.join(image_dir, image_files[random_idx])
    sample_mask_path = os.path.join(mask_dir, mask_files[random_idx])

    sample_image = cv2.imread(sample_image_path)
    sample_mask = cv2.imread(sample_mask_path, cv2.IMREAD_GRAYSCALE)

    plt.subplot(num_samples, 2, 2*i+1)
    plt.imshow(cv2.cvtColor(sample_image, cv2.COLOR_BGR2RGB))
    plt.title(f"Image {i+1}")
    plt.axis('off')

    plt.subplot(num_samples, 2, 2*i+2)
    plt.imshow(sample_mask, cmap='gray')
    plt.title(f"Mask {i+1}")
    plt.axis('off')

plt.tight_layout()
plt.show()
In [22]:
def load_images_and_masks(images_dir, masks_dir, img_size):
    image_files = next(os.walk(images_dir))[2]
    mask_files = next(os.walk(masks_dir))[2]

    images, masks = [], []
    for img_file, mask_file in zip(image_files, mask_files):
        img_path = os.path.join(images_dir, img_file)
        mask_path = os.path.join(masks_dir, mask_file)

        if os.path.exists(img_path) and os.path.exists(mask_path):
            # Load images and masks
            image = tf.keras.preprocessing.image.load_img(img_path, target_size=img_size)
            mask = tf.keras.preprocessing.image.load_img(mask_path, target_size=img_size, color_mode="grayscale")

            images.append(np.array(image))
            masks.append(np.array(mask)[:, :, np.newaxis])  # Add channel dimension
        else:
            print(f"Missing file: {img_file} or {mask_file}")

    return np.array(images, dtype=np.float32), np.array(masks, dtype=bool)

# Load data
X, y = load_images_and_masks(IMAGES_DIR, MASKS_DIR, IMG_SIZE)

# Split into train/test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

print(f"Training images: {X_train.shape}, Training masks: {y_train.shape}")
print(f"Testing images: {X_test.shape}, Testing masks: {y_test.shape}")
Training images: (953, 256, 256, 3), Training masks: (953, 256, 256, 1)
Testing images: (239, 256, 256, 3), Testing masks: (239, 256, 256, 1)
In [23]:
# Plot histogram of pixel intensities in the mask
plt.figure(figsize=(8, 6))
plt.hist(sample_mask.flatten(), bins=50, color='gray', alpha=0.7)
plt.title('Pixel Value Distribution in Mask')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.grid(True)
plt.show()
In the masks, the background colors are either black or white, that is why the histograms are showing 2 values 0 and 255¶

U-net Model architecture¶

In [24]:
inputs = tf.keras.layers.Input((256, 256, 3))
s = tf.keras.layers.Lambda(lambda x: x / 255)(inputs)

# Contraction path
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(s)
c1 = tf.keras.layers.Dropout(0.1)(c1)
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c1)
p1 = tf.keras.layers.MaxPooling2D((2, 2))(c1)

c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p1)
c2 = tf.keras.layers.Dropout(0.1)(c2)
c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c2)
p2 = tf.keras.layers.MaxPooling2D((2, 2))(c2)

c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p2)
c3 = tf.keras.layers.Dropout(0.2)(c3)
c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c3)
p3 = tf.keras.layers.MaxPooling2D((2, 2))(c3)

c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p3)
c4 = tf.keras.layers.Dropout(0.2)(c4)
c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c4)
p4 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(c4)

c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p4)
c5 = tf.keras.layers.Dropout(0.3)(c5)
c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c5)

# Expansive path
u6 = tf.keras.layers.Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c5)
u6 = tf.keras.layers.concatenate([u6, c4])
c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u6)
c6 = tf.keras.layers.Dropout(0.2)(c6)
c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c6)

u7 = tf.keras.layers.Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c6)
u7 = tf.keras.layers.concatenate([u7, c3])
c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u7)
c7 = tf.keras.layers.Dropout(0.2)(c7)
c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c7)

u8 = tf.keras.layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c7)
u8 = tf.keras.layers.concatenate([u8, c2])
c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u8)
c8 = tf.keras.layers.Dropout(0.1)(c8)
c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c8)

u9 = tf.keras.layers.Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c8)
u9 = tf.keras.layers.concatenate([u9, c1], axis=3)
c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u9)
c9 = tf.keras.layers.Dropout(0.1)(c9)
c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c9)

outputs = tf.keras.layers.Conv2D(1, (1, 1), activation='sigmoid')(c9)

model = tf.keras.Model(inputs=[inputs], outputs=[outputs])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model.summary()
WARNING:tensorflow:From c:\Users\binkh\Documents\CSCN8010\CSSN8010\venv\tensorflow_cpu\Lib\site-packages\keras\src\backend\tensorflow\core.py:222: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.

Model: "functional"
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃ Layer (type)        ┃ Output Shape      ┃    Param # ┃ Connected to      ┃
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ input_layer         │ (None, 256, 256,  │          0 │ -                 │
│ (InputLayer)        │ 3)                │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ lambda (Lambda)     │ (None, 256, 256,  │          0 │ input_layer[0][0] │
│                     │ 3)                │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d (Conv2D)     │ (None, 256, 256,  │        448 │ lambda[0][0]      │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout (Dropout)   │ (None, 256, 256,  │          0 │ conv2d[0][0]      │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_1 (Conv2D)   │ (None, 256, 256,  │      2,320 │ dropout[0][0]     │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d       │ (None, 128, 128,  │          0 │ conv2d_1[0][0]    │
│ (MaxPooling2D)      │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_2 (Conv2D)   │ (None, 128, 128,  │      4,640 │ max_pooling2d[0]… │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_1 (Dropout) │ (None, 128, 128,  │          0 │ conv2d_2[0][0]    │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_3 (Conv2D)   │ (None, 128, 128,  │      9,248 │ dropout_1[0][0]   │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d_1     │ (None, 64, 64,    │          0 │ conv2d_3[0][0]    │
│ (MaxPooling2D)      │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_4 (Conv2D)   │ (None, 64, 64,    │     18,496 │ max_pooling2d_1[… │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_2 (Dropout) │ (None, 64, 64,    │          0 │ conv2d_4[0][0]    │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_5 (Conv2D)   │ (None, 64, 64,    │     36,928 │ dropout_2[0][0]   │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d_2     │ (None, 32, 32,    │          0 │ conv2d_5[0][0]    │
│ (MaxPooling2D)      │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_6 (Conv2D)   │ (None, 32, 32,    │     73,856 │ max_pooling2d_2[… │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_3 (Dropout) │ (None, 32, 32,    │          0 │ conv2d_6[0][0]    │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_7 (Conv2D)   │ (None, 32, 32,    │    147,584 │ dropout_3[0][0]   │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d_3     │ (None, 16, 16,    │          0 │ conv2d_7[0][0]    │
│ (MaxPooling2D)      │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_8 (Conv2D)   │ (None, 16, 16,    │    295,168 │ max_pooling2d_3[… │
│                     │ 256)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_4 (Dropout) │ (None, 16, 16,    │          0 │ conv2d_8[0][0]    │
│                     │ 256)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_9 (Conv2D)   │ (None, 16, 16,    │    590,080 │ dropout_4[0][0]   │
│                     │ 256)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_transpose    │ (None, 32, 32,    │    131,200 │ conv2d_9[0][0]    │
│ (Conv2DTranspose)   │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate         │ (None, 32, 32,    │          0 │ conv2d_transpose… │
│ (Concatenate)       │ 256)              │            │ conv2d_7[0][0]    │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_10 (Conv2D)  │ (None, 32, 32,    │    295,040 │ concatenate[0][0] │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_5 (Dropout) │ (None, 32, 32,    │          0 │ conv2d_10[0][0]   │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_11 (Conv2D)  │ (None, 32, 32,    │    147,584 │ dropout_5[0][0]   │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_transpose_1  │ (None, 64, 64,    │     32,832 │ conv2d_11[0][0]   │
│ (Conv2DTranspose)   │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate_1       │ (None, 64, 64,    │          0 │ conv2d_transpose… │
│ (Concatenate)       │ 128)              │            │ conv2d_5[0][0]    │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_12 (Conv2D)  │ (None, 64, 64,    │     73,792 │ concatenate_1[0]… │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_6 (Dropout) │ (None, 64, 64,    │          0 │ conv2d_12[0][0]   │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_13 (Conv2D)  │ (None, 64, 64,    │     36,928 │ dropout_6[0][0]   │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_transpose_2  │ (None, 128, 128,  │      8,224 │ conv2d_13[0][0]   │
│ (Conv2DTranspose)   │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate_2       │ (None, 128, 128,  │          0 │ conv2d_transpose… │
│ (Concatenate)       │ 64)               │            │ conv2d_3[0][0]    │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_14 (Conv2D)  │ (None, 128, 128,  │     18,464 │ concatenate_2[0]… │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_7 (Dropout) │ (None, 128, 128,  │          0 │ conv2d_14[0][0]   │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_15 (Conv2D)  │ (None, 128, 128,  │      9,248 │ dropout_7[0][0]   │
│                     │ 32)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_transpose_3  │ (None, 256, 256,  │      2,064 │ conv2d_15[0][0]   │
│ (Conv2DTranspose)   │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate_3       │ (None, 256, 256,  │          0 │ conv2d_transpose… │
│ (Concatenate)       │ 32)               │            │ conv2d_1[0][0]    │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_16 (Conv2D)  │ (None, 256, 256,  │      4,624 │ concatenate_3[0]… │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout_8 (Dropout) │ (None, 256, 256,  │          0 │ conv2d_16[0][0]   │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_17 (Conv2D)  │ (None, 256, 256,  │      2,320 │ dropout_8[0][0]   │
│                     │ 16)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_18 (Conv2D)  │ (None, 256, 256,  │         17 │ conv2d_17[0][0]   │
│                     │ 1)                │            │                   │
└─────────────────────┴───────────────────┴────────────┴───────────────────┘
 Total params: 1,941,105 (7.40 MB)
 Trainable params: 1,941,105 (7.40 MB)
 Non-trainable params: 0 (0.00 B)
In [ ]:
# Train the model
history = model.fit(
    X_train, y_train,
    validation_data=(X_test, y_test),
    epochs=60,
    batch_size=16
)

# Save the model
model.save("segmentation_model.h5")
print("Model saved as segmentation_model.h5")
Epoch 1/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 43s 715ms/step - accuracy: 0.9576 - loss: 0.1100 - val_accuracy: 0.9671 - val_loss: 0.1010
Epoch 2/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 682ms/step - accuracy: 0.9671 - loss: 0.1052 - val_accuracy: 0.9706 - val_loss: 0.0882
Epoch 3/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 680ms/step - accuracy: 0.9752 - loss: 0.0696 - val_accuracy: 0.9773 - val_loss: 0.0725
Epoch 4/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 682ms/step - accuracy: 0.9817 - loss: 0.0518 - val_accuracy: 0.9803 - val_loss: 0.0659
Epoch 5/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 687ms/step - accuracy: 0.9829 - loss: 0.0489 - val_accuracy: 0.9760 - val_loss: 0.0735
Epoch 6/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 682ms/step - accuracy: 0.9830 - loss: 0.0498 - val_accuracy: 0.9831 - val_loss: 0.0645
Epoch 7/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 685ms/step - accuracy: 0.9801 - loss: 0.0750 - val_accuracy: 0.9850 - val_loss: 0.0609
Epoch 8/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 690ms/step - accuracy: 0.9815 - loss: 0.0731 - val_accuracy: 0.9855 - val_loss: 0.0582
Epoch 9/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 683ms/step - accuracy: 0.9846 - loss: 0.0549 - val_accuracy: 0.9862 - val_loss: 0.0608
Epoch 10/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 681ms/step - accuracy: 0.9887 - loss: 0.0318 - val_accuracy: 0.9828 - val_loss: 0.0639
Epoch 11/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 689ms/step - accuracy: 0.9847 - loss: 0.0510 - val_accuracy: 0.9860 - val_loss: 0.0598
Epoch 12/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 681ms/step - accuracy: 0.9880 - loss: 0.0383 - val_accuracy: 0.9866 - val_loss: 0.0617
Epoch 13/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 682ms/step - accuracy: 0.9892 - loss: 0.0330 - val_accuracy: 0.9846 - val_loss: 0.0564
Epoch 14/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 683ms/step - accuracy: 0.9892 - loss: 0.0316 - val_accuracy: 0.9867 - val_loss: 0.0580
Epoch 15/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 684ms/step - accuracy: 0.9878 - loss: 0.0420 - val_accuracy: 0.9877 - val_loss: 0.0556
Epoch 16/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 689ms/step - accuracy: 0.9911 - loss: 0.0251 - val_accuracy: 0.9858 - val_loss: 0.0592
Epoch 17/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 689ms/step - accuracy: 0.9881 - loss: 0.0388 - val_accuracy: 0.9880 - val_loss: 0.0594
Epoch 18/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 679ms/step - accuracy: 0.9860 - loss: 0.0477 - val_accuracy: 0.9885 - val_loss: 0.0644
Epoch 19/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 679ms/step - accuracy: 0.9902 - loss: 0.0326 - val_accuracy: 0.9883 - val_loss: 0.0635
Epoch 20/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 681ms/step - accuracy: 0.9919 - loss: 0.0226 - val_accuracy: 0.9864 - val_loss: 0.0508
Epoch 21/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 680ms/step - accuracy: 0.9896 - loss: 0.0320 - val_accuracy: 0.9882 - val_loss: 0.0559
Epoch 22/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 681ms/step - accuracy: 0.9923 - loss: 0.0211 - val_accuracy: 0.9841 - val_loss: 0.0578
Epoch 23/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 680ms/step - accuracy: 0.9898 - loss: 0.0323 - val_accuracy: 0.9888 - val_loss: 0.0689
Epoch 24/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 689ms/step - accuracy: 0.9920 - loss: 0.0235 - val_accuracy: 0.9885 - val_loss: 0.0574
Epoch 25/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 678ms/step - accuracy: 0.9887 - loss: 0.0376 - val_accuracy: 0.9895 - val_loss: 0.0773
Epoch 26/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 677ms/step - accuracy: 0.9913 - loss: 0.0285 - val_accuracy: 0.9880 - val_loss: 0.0627
Epoch 27/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 677ms/step - accuracy: 0.9906 - loss: 0.0290 - val_accuracy: 0.9888 - val_loss: 0.0554
Epoch 28/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 686ms/step - accuracy: 0.9917 - loss: 0.0255 - val_accuracy: 0.9893 - val_loss: 0.0776
Epoch 29/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 42s 698ms/step - accuracy: 0.9883 - loss: 0.0377 - val_accuracy: 0.9894 - val_loss: 0.0652
Epoch 30/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 687ms/step - accuracy: 0.9914 - loss: 0.0267 - val_accuracy: 0.9895 - val_loss: 0.0866
Epoch 31/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 684ms/step - accuracy: 0.9916 - loss: 0.0265 - val_accuracy: 0.9898 - val_loss: 0.0784
Epoch 32/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 685ms/step - accuracy: 0.9931 - loss: 0.0200 - val_accuracy: 0.9896 - val_loss: 0.0771
Epoch 33/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 680ms/step - accuracy: 0.9934 - loss: 0.0186 - val_accuracy: 0.9879 - val_loss: 0.0674
Epoch 34/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 688ms/step - accuracy: 0.9900 - loss: 0.0313 - val_accuracy: 0.9894 - val_loss: 0.0708
Epoch 35/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 685ms/step - accuracy: 0.9892 - loss: 0.0330 - val_accuracy: 0.9900 - val_loss: 0.0917
Epoch 36/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 42s 694ms/step - accuracy: 0.9913 - loss: 0.0294 - val_accuracy: 0.9899 - val_loss: 0.0677
Epoch 37/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 682ms/step - accuracy: 0.9916 - loss: 0.0259 - val_accuracy: 0.9902 - val_loss: 0.0901
Epoch 38/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 680ms/step - accuracy: 0.9927 - loss: 0.0214 - val_accuracy: 0.9894 - val_loss: 0.1020
Epoch 39/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 689ms/step - accuracy: 0.9935 - loss: 0.0190 - val_accuracy: 0.9902 - val_loss: 0.0840
Epoch 40/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 679ms/step - accuracy: 0.9913 - loss: 0.0262 - val_accuracy: 0.9900 - val_loss: 0.0947
Epoch 41/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 678ms/step - accuracy: 0.9879 - loss: 0.0434 - val_accuracy: 0.9896 - val_loss: 0.0883
Epoch 42/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 685ms/step - accuracy: 0.9885 - loss: 0.0438 - val_accuracy: 0.9900 - val_loss: 0.0847
Epoch 43/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 685ms/step - accuracy: 0.9895 - loss: 0.0426 - val_accuracy: 0.9894 - val_loss: 0.0644
Epoch 44/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 677ms/step - accuracy: 0.9921 - loss: 0.0246 - val_accuracy: 0.9893 - val_loss: 0.0764
Epoch 45/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 683ms/step - accuracy: 0.9921 - loss: 0.0240 - val_accuracy: 0.9903 - val_loss: 0.0739
Epoch 46/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 687ms/step - accuracy: 0.9924 - loss: 0.0226 - val_accuracy: 0.9904 - val_loss: 0.0865
Epoch 47/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 42s 702ms/step - accuracy: 0.9919 - loss: 0.0241 - val_accuracy: 0.9901 - val_loss: 0.0813
Epoch 48/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 682ms/step - accuracy: 0.9936 - loss: 0.0182 - val_accuracy: 0.9901 - val_loss: 0.0965
Epoch 49/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 682ms/step - accuracy: 0.9929 - loss: 0.0205 - val_accuracy: 0.9904 - val_loss: 0.1008
Epoch 50/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 684ms/step - accuracy: 0.9940 - loss: 0.0170 - val_accuracy: 0.9886 - val_loss: 0.0839
Epoch 51/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 681ms/step - accuracy: 0.9924 - loss: 0.0221 - val_accuracy: 0.9905 - val_loss: 0.0963
Epoch 52/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 681ms/step - accuracy: 0.9926 - loss: 0.0230 - val_accuracy: 0.9902 - val_loss: 0.0854
Epoch 53/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 685ms/step - accuracy: 0.9935 - loss: 0.0187 - val_accuracy: 0.9902 - val_loss: 0.0974
Epoch 54/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 685ms/step - accuracy: 0.9909 - loss: 0.0262 - val_accuracy: 0.9908 - val_loss: 0.1228
Epoch 55/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 42s 693ms/step - accuracy: 0.9947 - loss: 0.0153 - val_accuracy: 0.9872 - val_loss: 0.0632
Epoch 56/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 686ms/step - accuracy: 0.9920 - loss: 0.0227 - val_accuracy: 0.9889 - val_loss: 0.0588
Epoch 57/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 682ms/step - accuracy: 0.9936 - loss: 0.0188 - val_accuracy: 0.9898 - val_loss: 0.0789
Epoch 58/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 681ms/step - accuracy: 0.9914 - loss: 0.0268 - val_accuracy: 0.9903 - val_loss: 0.0780
Epoch 59/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 681ms/step - accuracy: 0.9915 - loss: 0.0254 - val_accuracy: 0.9908 - val_loss: 0.1111
Epoch 60/60
60/60 ━━━━━━━━━━━━━━━━━━━━ 41s 683ms/step - accuracy: 0.9915 - loss: 0.0296 - val_accuracy: 0.9891 - val_loss: 0.0517
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`. 
Model saved as segmentation_model.h5

Some other external images¶

In [ ]:
import matplotlib.pyplot as plt

# Assuming `results` contains the output of model.fit()
# Plot accuracy
plt.figure(figsize=(10, 5))

plt.subplot(1, 2, 1)  # Create two plots side by side
plt.plot(history.history['accuracy'], label='Train Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy', linestyle='--')
plt.title('Model Accuracy', fontsize=16)
plt.xlabel('Epoch', fontsize=12)
plt.ylabel('Accuracy', fontsize=12)
plt.legend(loc='lower right', fontsize=10)
plt.grid(visible=True)

# Plot loss
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Validation Loss', linestyle='--')
plt.title('Model Loss', fontsize=16)
plt.xlabel('Epoch', fontsize=12)
plt.ylabel('Loss', fontsize=12)
plt.legend(loc='upper right', fontsize=10)
plt.grid(visible=True)

plt.tight_layout()  # Adjust spacing between plots
plt.show()
In [45]:
from tensorflow.keras.models import load_model

# saved model loaded
saved_model = load_model("segmentation_model.h5")
print("Model loaded successfully!")

# predict using a custom image
custom_image_path = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/test/test7.WEBP"
custom_image = tf.keras.preprocessing.image.load_img(custom_image_path, target_size=IMG_SIZE)
custom_image_array = np.expand_dims(np.array(custom_image) / 255.0, axis=0)

# mask prediction
predicted_mask = saved_model.predict(custom_image_array)

# visualizations
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Input Image")
plt.imshow(custom_image)
plt.axis('off')

plt.subplot(1, 2, 2)
plt.title("Predicted Mask")
plt.imshow(predicted_mask[0, :, :, 0])
WARNING:absl:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
Model loaded successfully!
WARNING:tensorflow:5 out of the last 5 calls to <function TensorFlowTrainer.make_predict_function.<locals>.one_step_on_data_distributed at 0x000002286B50FE20> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
WARNING:tensorflow:5 out of the last 5 calls to <function TensorFlowTrainer.make_predict_function.<locals>.one_step_on_data_distributed at 0x000002286B50FE20> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 233ms/step
Out[45]:
<matplotlib.image.AxesImage at 0x22867b4c450>
In [ ]:
from tensorflow.keras.models import load_model

# saved model loaded
saved_model = load_model("segmentation_model.h5")
print("Model loaded successfully!")

# predict using a custom image
custom_image_path = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/test/test3.webp"
custom_image = tf.keras.preprocessing.image.load_img(custom_image_path, target_size=IMG_SIZE)
custom_image_array = np.expand_dims(np.array(custom_image) / 255.0, axis=0)

# mask prediction
predicted_mask = saved_model.predict(custom_image_array)

# visualizations
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Input Image")
plt.imshow(custom_image)
plt.axis('off')

plt.subplot(1, 2, 2)
plt.title("Predicted Mask")
plt.imshow(predicted_mask[0, :, :, 0])
WARNING:absl:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
Model loaded successfully!
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 220ms/step
<matplotlib.image.AxesImage at 0x275c6803c10>
In [62]:
from tensorflow.keras.models import load_model

# saved model loaded
saved_model = load_model("segmentation_model.h5")
print("Model loaded successfully!")

# predict using a custom image
custom_image_path = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/test/test14.JPEG"
custom_image = tf.keras.preprocessing.image.load_img(custom_image_path, target_size=IMG_SIZE)
custom_image_array = np.expand_dims(np.array(custom_image) / 255.0, axis=0)

# mask prediction
predicted_mask = saved_model.predict(custom_image_array)

# visualizations
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Input Image")
plt.imshow(custom_image)
plt.axis('off')

plt.subplot(1, 2, 2)
plt.title("Predicted Mask")
plt.imshow(predicted_mask[0, :, :, 0])
WARNING:absl:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
Model loaded successfully!
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 227ms/step
Out[62]:
<matplotlib.image.AxesImage at 0x228678868d0>
In [64]:
from tensorflow.keras.models import load_model

# saved model loaded
saved_model = load_model("segmentation_model.h5")
print("Model loaded successfully!")

# predict using a custom image
custom_image_path = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/test/test16.JPEG"
custom_image = tf.keras.preprocessing.image.load_img(custom_image_path, target_size=IMG_SIZE)
custom_image_array = np.expand_dims(np.array(custom_image) / 255.0, axis=0)

# mask prediction
predicted_mask = saved_model.predict(custom_image_array)

# visualizations
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Input Image")
plt.imshow(custom_image)
plt.axis('off')

plt.subplot(1, 2, 2)
plt.title("Predicted Mask")
plt.imshow(predicted_mask[0, :, :, 0])
WARNING:absl:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
Model loaded successfully!
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 233ms/step
Out[64]:
<matplotlib.image.AxesImage at 0x2286b57fb10>
In [26]:
from tensorflow.keras.models import load_model

# saved model loaded
saved_model = load_model("segmentation_model.h5")
print("Model loaded successfully!")

# predict using a custom image
custom_image_path = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/test/test5.JPEG"
custom_image = tf.keras.preprocessing.image.load_img(custom_image_path, target_size=IMG_SIZE)
custom_image_array = np.expand_dims(np.array(custom_image) / 255.0, axis=0)

# mask prediction
predicted_mask = saved_model.predict(custom_image_array)

# visualizations
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Input Image")
plt.imshow(custom_image)
plt.axis('off')

plt.subplot(1, 2, 2)
plt.title("Predicted Mask")
plt.imshow(predicted_mask[0, :, :, 0])
WARNING:absl:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
Model loaded successfully!
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 430ms/step
Out[26]:
<matplotlib.image.AxesImage at 0x2284dc3e810>
In [49]:
from tensorflow.keras.models import load_model

# saved model loaded
saved_model = load_model("segmentation_model.h5")
print("Model loaded successfully!")

# predict using a custom image
custom_image_path = "C:/Users/binkh/Documents/CSCN8010/CSCN8010-labs/Humseg/segmentation_full_body_mads_dataset_1192_img/test/test8.JPEG"
custom_image = tf.keras.preprocessing.image.load_img(custom_image_path, target_size=IMG_SIZE)
custom_image_array = np.expand_dims(np.array(custom_image) / 255.0, axis=0)

# mask prediction
predicted_mask = saved_model.predict(custom_image_array)

# visualizations
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Input Image")
plt.imshow(custom_image)
plt.axis('off')

plt.subplot(1, 2, 2)
plt.title("Predicted Mask")
plt.imshow(predicted_mask[0, :, :, 0])
WARNING:absl:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
Model loaded successfully!
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 212ms/step
Out[49]:
<matplotlib.image.AxesImage at 0x2286b2f3910>